use std::{io::Cursor, sync::Arc};

use anyhow::Error;
use log::{debug, error, info, warn};
use quick_xml::{
    events::{BytesStart, Event},
    name::{Namespace, QName, ResolveResult},
    NsReader, Writer,
};
use yang2::{
    context::Context,
    data::{Data, DataNodeRef, DataPrinterFlags, DataTree},
};

use crate::{
    EncodeString, ErrorInfo, ErrorOperationType, ErrorSeverity, ErrorTag, ErrorType, NcMsg,
    NcMsgCodec, NcopMsg, OperationType, SourceType, TestOptionType, TreeDecoder, XmlEncodeAble,
};

#[derive(Debug, Clone)]
pub struct XmlNcMsgCodec {
    pub ctx: Arc<Context>,
}

const NETCONF_BASE: &[u8] = b"urn:ietf:params:xml:ns:netconf:base:1.0";
const NETCONF_NOTIFICATION_BASE: &[u8] = b"urn:ietf:params:xml:ns:netconf:notification:1.0";
const NETCONF_OP_RPC: &[u8] = b"rpc";
const NETCONF_OP_HELLO: &[u8] = b"hello";
const NETCONF_OP_NOTIFICATION: &[u8] = b"notification";
const NETCONF_OP_RPC_REPLY: &[u8] = b"rpc-reply";

pub const NETCONF_HELLO_END: &[u8] = b"]]>]]>";

fn decode_subtree_filter_path(content: String, prefix: &str) -> Vec<String> {
    let mut paths = vec![];
    let mut reader = NsReader::from_str(&content);
    decode_path_from_reader(&mut paths, &mut reader, prefix.to_string());
    debug!("filter paths: {:?}", paths);
    paths
}

fn decode_path_from_reader(paths: &mut Vec<String>, reader: &mut NsReader<&[u8]>, prefix: String) {
    match reader.read_resolved_event() {
        Ok((_, Event::Start(fb))) => {
            let tag_name = String::from_utf8(fb.name().0.to_vec()).unwrap();
            decode_path_from_reader(paths, reader, format!("{}/{}", prefix, tag_name));
        }
        Ok((_, Event::Text(fb))) => {
            let value = String::from_utf8(fb.into_inner().to_vec())
                .unwrap()
                .trim()
                .to_string();
            if value.len() > 0 {
                let new_path = format!("{}[.='{}']", prefix, value);
                paths.push(new_path.clone());
                decode_path_from_reader(paths, reader, new_path);
            } else {
                decode_path_from_reader(paths, reader, prefix);
            }
        }
        Ok((_, Event::Empty(fb))) => {
            let tag_name = String::from_utf8(fb.to_end().name().0.to_vec()).unwrap();
            let path = format!("{}/{}", prefix, tag_name);
            paths.push(path);
            decode_path_from_reader(paths, reader, prefix);
        }
        _ => (),
    }
}

impl XmlNcMsgCodec {
    fn decode_nc_msg(&self, data: &str) -> NcMsg {
        let mut reader = NsReader::from_str(data);
        reader.config_mut().trim_text(true);
        loop {
            if let Ok(evt) = reader.read_event() {
                match evt {
                    Event::Start(e) => {
                        let (ele, name) = reader.resolve_element(e.name());
                        let tag_name = name.into_inner();
                        debug!(
                            "tag: {:?}, result: {:?}",
                            String::from_utf8(tag_name.to_vec()).unwrap(),
                            ele
                        );
                        if let ResolveResult::Bound(a) = ele {
                            if a.0.ne(NETCONF_BASE) {
                                if a.0.ne(NETCONF_NOTIFICATION_BASE) {
                                    debug!("invalid netconf namespace, {:?}", a.0);
                                    return NcMsg::InvelidMsg(
                                        "".to_string(),
                                        "invalid namespace".to_string(),
                                    );
                                }
                            }
                        }
                        if tag_name.eq(NETCONF_OP_HELLO) {
                            return NcMsg::Hello(crate::Hello {
                                capabilities: crate::Capabilities {},
                                session_id: 0,
                            });
                        }
                        if tag_name.eq(NETCONF_OP_RPC) {
                            if let Ok(Some(msg_id_attr)) = e.try_get_attribute("message-id") {
                                let message_id =
                                    String::from_utf8(msg_id_attr.value.to_vec()).unwrap();
                                if let Ok(text) = reader.read_text(e.to_end().name()) {
                                    if let Ok(str) =
                                        String::from_utf8(text.into_owned().as_bytes().to_vec())
                                    {
                                        return self.decode_rpc_msg(
                                            &message_id,
                                            &self.fillup_default_namespace(&str),
                                        );
                                    }
                                }
                            } else {
                                return NcMsg::InvelidMsg(
                                    "".to_string(),
                                    "invalid message-id attr".to_string(),
                                );
                            }
                        }
                        if tag_name.eq(NETCONF_OP_NOTIFICATION) {
                            let str = String::from_utf8(
                                reader
                                    .read_text(e.to_end().name())
                                    .unwrap()
                                    .into_owned()
                                    .as_bytes()
                                    .to_vec(),
                            )
                            .unwrap();
                            return self.decode_notify_msg(&self.fillup_default_namespace(&str));
                        }
                        if tag_name.eq(NETCONF_OP_RPC_REPLY) {
                            let message_id = e
                                .attributes()
                                .find(|attr| {
                                    if let Ok(attr) = attr {
                                        if attr.key.eq(&QName(b"message-id")) {
                                            return true;
                                        }
                                    }
                                    false
                                })
                                .unwrap()
                                .unwrap();
                            match reader.read_resolved_event() {
                                Ok((
                                    ResolveResult::Bound(Namespace(NETCONF_BASE)),
                                    Event::Start(bs),
                                )) => {
                                    let tag_name = String::from_utf8(bs.name().0.to_vec()).unwrap();
                                    let message_id_str =
                                        String::from_utf8(message_id.value.to_vec()).unwrap();
                                    if tag_name.eq("data") {
                                        let content = String::from_utf8(
                                            reader
                                                .read_text(bs.to_end().name())
                                                .unwrap()
                                                .as_bytes()
                                                .to_vec(),
                                        )
                                        .unwrap();
                                        return NcMsg::RpcReply(
                                            message_id_str,
                                            NcopMsg::Data(content),
                                        );
                                    }

                                    if tag_name.eq("ok") {
                                        return NcMsg::RpcReply(message_id_str, NcopMsg::Ok);
                                    } else if tag_name.eq("rpc-error") {
                                        let mut error_type = ErrorType::Protocol;
                                        let mut error_tag = ErrorTag::BadElement;
                                        let mut error_severity = ErrorSeverity::Error;
                                        let mut error_info = ErrorInfo {
                                            content: "".to_string(),
                                        };
                                        loop {
                                            match reader.read_resolved_event() {
                                                Ok((_, Event::Start(bs))) => {
                                                    if bs
                                                        .local_name()
                                                        .eq(&QName(b"error-type").into())
                                                    {
                                                        let type_str = String::from_utf8(
                                                            reader
                                                                .read_text(bs.to_end().name())
                                                                .unwrap()
                                                                .as_bytes()
                                                                .to_vec(),
                                                        )
                                                        .unwrap();
                                                        error_type = ErrorType::from_str(&type_str);
                                                    } else if bs
                                                        .local_name()
                                                        .eq(&QName(b"error-tag").into())
                                                    {
                                                        let tag_str = String::from_utf8(
                                                            reader
                                                                .read_text(bs.to_end().name())
                                                                .unwrap()
                                                                .as_bytes()
                                                                .to_vec(),
                                                        )
                                                        .unwrap();
                                                        error_tag = ErrorTag::from_str(&tag_str);
                                                    } else if bs
                                                        .local_name()
                                                        .eq(&QName(b"error-severity").into())
                                                    {
                                                        let severity_str = String::from_utf8(
                                                            reader
                                                                .read_text(bs.to_end().name())
                                                                .unwrap()
                                                                .as_bytes()
                                                                .to_vec(),
                                                        )
                                                        .unwrap();
                                                        error_severity =
                                                            ErrorSeverity::from_str(&severity_str);
                                                    } else if bs
                                                        .local_name()
                                                        .eq(&QName(b"error-info").into())
                                                    {
                                                        let context_str = String::from_utf8(
                                                            reader
                                                                .read_text(bs.to_end().name())
                                                                .unwrap()
                                                                .as_bytes()
                                                                .to_vec(),
                                                        )
                                                        .unwrap();
                                                        error_info.content =
                                                            context_str.trim().to_string();
                                                    }
                                                }
                                                Ok((_, Event::Eof)) => break,
                                                Err(_) => break,
                                                _ => (),
                                            }
                                        }
                                        return NcMsg::RpcReply(
                                            message_id_str,
                                            NcopMsg::RpcError(
                                                error_type,
                                                error_tag,
                                                error_severity,
                                                error_info,
                                            ),
                                        );
                                    }
                                    warn!("invalid rpc reply tag: {}", tag_name);
                                }
                                Ok((
                                    ResolveResult::Bound(Namespace(NETCONF_BASE)),
                                    Event::Empty(bs),
                                )) => {
                                    let tag_name = String::from_utf8(bs.name().0.to_vec()).unwrap();
                                    let message_id_str =
                                        String::from_utf8(message_id.value.to_vec()).unwrap();
                                    if tag_name.eq("ok") {
                                        return NcMsg::RpcReply(message_id_str, NcopMsg::Ok);
                                    }
                                    warn!("invalid rpc reply empty tag: {}", tag_name);
                                }
                                _ => {
                                    error!("decode rpc reply msg failed");
                                }
                            }
                        }
                        return NcMsg::InvelidMsg("".to_string(), "invalid nc message".to_string());
                    }
                    Event::Text(_e) => {
                        println!("content event")
                    }
                    Event::Eof => break,
                    _ => (),
                }
            }
        }
        NcMsg::InvelidMsg("".to_string(), "invalid nc message".to_string())
    }

    fn decode_rpc_msg(&self, message_id: &str, data: &str) -> NcMsg {
        let tree = DataTree::parse_op_string(
            &self.ctx,
            &data,
            yang2::data::DataFormat::XML,
            yang2::data::DataOperation::RpcYang,
        );
        match tree {
            Ok(tree) => {
                if let Some(top_node) = tree.reference() {
                    match top_node.owner_module().namespace() {
                        "urn:ietf:params:xml:ns:netconf:base:1.0" => {
                            match top_node.schema().name() {
                                "get" => match GetData::decode_from(&tree) {
                                    Ok(msg) => {
                                        return NcMsg::Rpc(
                                            message_id.to_string(),
                                            NcopMsg::Get(msg),
                                        )
                                    }
                                    _ => {
                                        return NcMsg::InvelidMsg(
                                            message_id.to_string(),
                                            "decode get-data failed".to_string(),
                                        )
                                    }
                                },
                                "get-config" => match GetConfigData::decode_from(&tree) {
                                    Ok(msg) => {
                                        return NcMsg::Rpc(
                                            message_id.to_string(),
                                            NcopMsg::GetConfig(msg),
                                        )
                                    }
                                    _ => {
                                        return NcMsg::InvelidMsg(
                                            message_id.to_string(),
                                            "decode get-config-data failed".to_string(),
                                        )
                                    }
                                },
                                "edit-config" => match EditConfigData::decode_from(&tree) {
                                    Ok(msg) => {
                                        return NcMsg::Rpc(
                                            message_id.to_string(),
                                            NcopMsg::EditConfig(msg),
                                        )
                                    }
                                    _ => {
                                        return NcMsg::InvelidMsg(
                                            message_id.to_string(),
                                            "decode get-config-data failed".to_string(),
                                        )
                                    }
                                },
                                "copy-config" => match CopyConfigData::decode_from(&tree) {
                                    Ok(msg) => {
                                        return NcMsg::Rpc(
                                            message_id.to_string(),
                                            NcopMsg::CopyConfig(msg),
                                        )
                                    }
                                    _ => {
                                        return NcMsg::InvelidMsg(
                                            message_id.to_string(),
                                            "decode copy-config-data failed".to_string(),
                                        );
                                    }
                                },
                                "delete-config" => match DeleteConfigData::decode_from(&tree) {
                                    Ok(msg) => {
                                        return NcMsg::Rpc(
                                            message_id.to_string(),
                                            NcopMsg::DeleteConfig(msg),
                                        );
                                    }
                                    _ => {
                                        return NcMsg::InvelidMsg(
                                            message_id.to_string(),
                                            "decode copy-config-data failed".to_string(),
                                        );
                                    }
                                },
                                "close-session" => {
                                    return NcMsg::Rpc(
                                        message_id.to_string(),
                                        NcopMsg::CloseSession,
                                    );
                                }
                                "kill-session" => {
                                    return NcMsg::Rpc(
                                        message_id.to_string(),
                                        NcopMsg::KillSession,
                                    );
                                }
                                _ => {
                                    return NcMsg::InvelidMsg(
                                        message_id.to_string(),
                                        format!(
                                            "invalid nc operation: {}",
                                            top_node.schema().name()
                                        ),
                                    );
                                }
                            }
                        }
                        _ => {
                            // decode rpc call
                            let namespace = top_node.owner_module().namespace().to_string();
                            let rpc_name = top_node.schema().name().to_string();
                            return NcMsg::Rpc(
                                message_id.to_string(),
                                NcopMsg::RpcCall(namespace, rpc_name, Arc::new(tree)),
                            );
                        }
                    }
                }
                NcMsg::InvelidMsg(
                    message_id.to_string(),
                    "there is no top level node".to_string(),
                )
            }
            Err(e) => {
                error!("parse op string to tree failed, {}", e.to_string());
                return NcMsg::InvelidMsg(message_id.to_string(), format!("{}", e.to_string()));
            }
        }
    }

    fn decode_notify_msg(&self, data: &str) -> NcMsg {
        let tree = DataTree::parse_op_string(
            &self.ctx,
            &data,
            yang2::data::DataFormat::XML,
            yang2::data::DataOperation::NotificationYang,
        );
        match tree {
            Ok(tree) => {
                let string = tree
                    .print_string(yang2::data::DataFormat::JSON, DataPrinterFlags::empty())
                    .unwrap()
                    .unwrap();
                info!("parse notify string to tree success,{}", string);
            }
            Err(e) => {
                error!("parse notify string to tree failed, {}", e.to_string());
                return NcMsg::InvelidMsg(
                    "".to_string(),
                    "parse op string to tree failed".to_string(),
                );
            }
        }
        NcMsg::InvelidMsg("".to_string(), "decode ncmsg failed".to_string())
    }

    fn fillup_default_namespace(&self, data: &str) -> String {
        let mut reader = NsReader::from_str(data);
        reader.config_mut().trim_text(true);
        let mut writer = Writer::new(Cursor::new(Vec::new()));
        let mut added = false;
        loop {
            match reader.read_resolved_event() {
                Ok((ResolveResult::Unbound, Event::Start(bs))) => {
                    let tag_name =
                        String::from_utf8(bs.local_name().into_inner().to_vec()).unwrap();
                    let mut elem = BytesStart::new(&tag_name);
                    // collect existing attributes
                    elem.extend_attributes(bs.attributes().map(|attr| attr.unwrap()));
                    // copy existing attributes, adds a new my-key="some value" attribute
                    if !added {
                        elem.push_attribute(("xmlns", "urn:ietf:params:xml:ns:netconf:base:1.0"));
                        added = true;
                    }
                    writer.write_event(Event::Start(elem)).unwrap();
                }
                Ok((ResolveResult::Unbound, Event::Empty(bs))) => {
                    let tag_name =
                        String::from_utf8(bs.local_name().into_inner().to_vec()).unwrap();
                    let mut elem = BytesStart::new(&tag_name);
                    // collect existing attributes
                    elem.extend_attributes(bs.attributes().map(|attr| attr.unwrap()));
                    // copy existing attributes, adds a new my-key="some value" attribute
                    if !added {
                        elem.push_attribute(("xmlns", "urn:ietf:params:xml:ns:netconf:base:1.0"));
                        added = true;
                    }
                    writer.write_event(Event::Empty(elem)).unwrap();
                }
                Ok((_, evt)) => {
                    writer.write_event(evt.clone()).unwrap();
                    if let Event::Eof = evt {
                        break;
                    }
                }
                _ => break,
            }
        }

        String::from_utf8(writer.into_inner().into_inner()).unwrap()
    }
}

impl NcMsgCodec for XmlNcMsgCodec {
    fn decode(&mut self, data: &[u8]) -> (usize, Option<Vec<crate::NcMsg>>) {
        let mut pos: usize = 0;
        let mut msgs = vec![];
        let mut d = vec![];

        'looper: while pos <= data.len() {
            let mut found = false;
            'outer: for i in pos..data.len() {
                match data[i] {
                    0x0A => {
                        if i + 4 <= data.len() {
                            if data[i..i + 4].eq(&[0x0A, 0x23, 0x23, 0x0A]) {
                                pos = i + 4;
                                let ds = String::from_utf8(d.clone()).unwrap();
                                d.clear();
                                msgs.push(self.decode_nc_msg(&ds));
                            }
                        } else {
                            break;
                        }
                        if i == data.len() {
                            continue;
                        }
                        if data[i + 1].eq(&0x23) {
                            for j in i + 2..data.len() {
                                if data[j].eq(&0x0A) {
                                    let len = String::from_utf8(data[i + 2..j].to_vec())
                                        .unwrap()
                                        .parse::<usize>()
                                        .unwrap();
                                    if j + len <= data.len() {
                                        d.append(&mut data[j + 1..j + 1 + len].to_vec());
                                        pos = j + len;
                                        found = true;
                                        break 'outer;
                                    }
                                } else if data[j] < 0x30 || data[j] > 0x39 {
                                    break;
                                }
                            }
                        }
                    }
                    0x5D => {
                        if i + 6 <= data.len() {
                            if data[i..i + 6].eq(&[0x5D, 0x5D, 0x3E, 0x5D, 0x5D, 0x3E]) {
                                let ds = String::from_utf8(data[pos..i].to_vec()).unwrap();
                                pos = i + 6;
                                msgs.push(self.decode_nc_msg(&ds));
                                found = true;
                                break 'outer;
                            }
                        }
                    }
                    _ => (),
                }
            }
            if !found {
                break 'looper;
            }
        }
        (pos, Some(msgs))
    }

    fn encode(&mut self, msg: &crate::NcMsg) -> Vec<u8> {
        match msg {
            NcMsg::Hello( hello) => {
                format!(r#"<?xml version="1.0" encoding="UTF-8"?>{}"#,hello.encode_xml())
                .as_bytes()
                .to_vec()
                // b"".to_vec()
            }
            NcMsg::Rpc(id,  msg) => format!(
                r#"<?xml version="1.0" encoding="UTF-8"?>
                <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="{}">{}</rpc-reply>
                "#,
                id,
                msg.encode_xml()
            )
            .as_bytes()
            .to_vec(),
            NcMsg::RpcReply(id,  msg) => format!(
                r#"<?xml version="1.0" encoding="UTF-8"?>
                <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="{}">{}</rpc-reply>
                "#,
                id,
                msg.encode_xml()
            )
            .as_bytes()
            .to_vec(),
            NcMsg::InvelidMsg(msg_id, reason) => format!(
                r#"<?xml version="1.0" encoding="UTF-8"?>
                <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="{}">{}</rpc-reply>
                "#,
                msg_id,
                NcopMsg::RpcError(ErrorType::Application, ErrorTag::BadAttribute, ErrorSeverity::Error, ErrorInfo { content: reason.clone() }).encode_xml()
            )
            .as_bytes()
            .to_vec(),
            _ => vec![],
        }
    }
}

impl XmlEncodeAble for NcopMsg {
    fn encode_xml(&self) -> String {
        match self {
            NcopMsg::CloseSession => "<close-session/>".to_string(),
            NcopMsg::KillSession => "<kill-session/>".to_string(),
            NcopMsg::Get(d) => format!("<get>{:?}</get>", d.filter_paths),
            NcopMsg::GetConfig(data) => format!(
                r#"
                <get-config>
                    <source>{}</source>
                    <filter>{:?}</filter>
                </get-config>
                "#,
                data.source.encode_string(),
                data.filter_paths
            ),
            NcopMsg::EditConfig(ec) => {
                format!(
                    r#"
                    <edit-config>
                    <target>
                        {}
                    </target>
                    <default-operation>{}</default-operation>
                    <test-option>{}</test-option>
                    <error-option>{}</error-option>
                    <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
                    {}
                    </config>
                    </edit-config>
                    "#,
                    ec.target.encode_string(),
                    ec.default_operation.encode_string(),
                    ec.test_option.encode_string(),
                    ec.error_option.encode_string(),
                    ec.config
                )
            }
            NcopMsg::CopyConfig(copy_config_data) => format!(
                r#"<copy-config>
                    <target>
                        {}
                    </target>
                    <source>
                        {}
                    </source>
                </copy-config>"#,
                copy_config_data.target.encode_string(),
                copy_config_data.source.encode_string()
            ),
            NcopMsg::DeleteConfig(t) => format!(
                r#"<delete-config>
                <target>
                    {}
                </target>
                </delete-config>"#,
                t.target.encode_string()
            ),
            NcopMsg::Ok => "<ok/>".to_string(),
            NcopMsg::RpcError(t, tag, s, info) => format!(
                r#"
            <rpc-error>
                <error-type>{}</error-type>
                <error-tag>{}</error-tag>
                <error-severity>{}</error-severity>
                <error-info>
                    {}
                </error-info>
            </rpc-error>
            "#,
                t.encode_string(),
                tag.encode_string(),
                s.encode_string(),
                info.content
            ),
            NcopMsg::Data(content) => {
                format!(r#"<data>{}</data>"#, content)
            }
            NcopMsg::RpcCall(_ns, _name, content) => content
                .print_string(yang2::data::DataFormat::XML, DataPrinterFlags::empty())
                .unwrap()
                .unwrap(),
            NcopMsg::CreateSubscription => {
                r#"<create-subscription xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"/>"#
                    .to_string()
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct Agent {
    pub ip: String,
    pub ne_type: String,
    pub ignore_ip_check: bool,
}

impl Agent {
    pub fn from_node(agent_node: DataNodeRef) -> Self {
        let mut ip = String::from("");
        let mut ne_type = String::from("RCST");
        let mut ignore_ip_check = false;
        for ap in agent_node.children() {
            match ap.schema().name() {
                "ip" => ip = ap.value_canonical().unwrap(),
                "type" => ne_type = ap.value_canonical().unwrap(),
                "ignore_ip_check" => ignore_ip_check = ap.value_canonical().unwrap().eq("true"),
                _ => (),
            }
        }
        Agent {
            ip,
            ne_type,
            ignore_ip_check,
        }
    }
}

#[derive(Debug, Clone)]
pub struct GetConfigData {
    pub filter_paths: Vec<(String, String)>,
    pub source: SourceType,
}

impl TreeDecoder for GetConfigData {
    fn decode_from(tree: &DataTree) -> Result<Self, Error> {
        // decode source
        let mut source = SourceType::Running;
        if let Ok(source_node) = tree.find_path("/ietf-netconf:get-config/source") {
            for ele in source_node.children() {
                match ele.schema().name() {
                    "startup" => source = SourceType::Startup,
                    "running" => source = SourceType::Running,
                    "candidate" => source = SourceType::Candidate,
                    _ => (),
                }
            }
        }

        // decode filter params
        let mut filter_paths = vec![];
        if let Ok(filter_node) = tree.find_path("/ietf-netconf:get-config/filter") {
            decode_filter_nodes_to_paths(filter_node, &mut filter_paths)
        }

        Ok(GetConfigData {
            filter_paths,
            source,
        })
    }
}

#[derive(Debug, Clone)]
pub struct GetData {
    pub filter_paths: Vec<(String, String)>,
}

impl TreeDecoder for GetData {
    fn decode_from(tree: &DataTree) -> Result<Self, Error> {
        // decode filter params
        let mut filter_paths = vec![];
        if let Ok(filter_node) = tree.find_path("/ietf-netconf:get/filter") {
            decode_filter_nodes_to_paths(filter_node, &mut filter_paths)
        }
        Ok(GetData { filter_paths })
    }
}

// target,default-operation,test-option,error-option,edit-content
#[derive(Debug, Clone)]
pub struct EditConfigData {
    pub target: SourceType,
    pub default_operation: OperationType,
    pub test_option: TestOptionType,
    pub error_option: ErrorOperationType,
    pub config: String,
}

impl TreeDecoder for EditConfigData {
    fn decode_from(tree: &DataTree) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let mut target = SourceType::Running;
        let mut default_operation = OperationType::Merge;
        let mut test_option = TestOptionType::TestThenSet;
        let mut error_option = ErrorOperationType::StopOnError;

        // decode target
        if let Ok(target_node) = tree.find_path("/ietf-netconf:edit-config/target") {
            for ele in target_node.children() {
                match ele.schema().name() {
                    "startup" => target = SourceType::Startup,
                    "running" => target = SourceType::Running,
                    "candidate" => target = SourceType::Candidate,
                    _ => (),
                }
            }
        }

        // decode default-operation
        if let Ok(default_operation_node) =
            tree.find_path("/ietf-netconf:edit-config/default-operation")
        {
            if let Some(value_str) = default_operation_node.value_canonical() {
                match value_str.as_str() {
                    "merge" => default_operation = OperationType::Merge,
                    "none" => default_operation = OperationType::None,
                    "replace" => default_operation = OperationType::Replace,
                    _ => (),
                }
            }
        }

        // decode test-option
        if let Ok(test_option_node) = tree.find_path("/ietf-netconf:edit-config/test-option") {
            if let Some(value_str) = test_option_node.value_canonical() {
                match value_str.as_str() {
                    "test-then-set" => test_option = TestOptionType::TestThenSet,
                    "set" => test_option = TestOptionType::Set,
                    "test-only" => test_option = TestOptionType::TestOnly,
                    _ => (),
                }
            }
        }

        // decode error-option
        if let Ok(error_option_node) = tree.find_path("/ietf-netconf:edit-config/error-option") {
            if let Some(value_str) = error_option_node.value_canonical() {
                match value_str.as_str() {
                    "stop-on-error" => error_option = ErrorOperationType::ContinueOnError,
                    "continue-on-error" => error_option = ErrorOperationType::RollbackOnError,
                    "rollback-on-error" => error_option = ErrorOperationType::StopOnError,
                    _ => (),
                }
            }
        }

        // decode config content
        if let Ok(config_node) = tree.find_path("/ietf-netconf:edit-config/config") {
            if let Ok(Some(config_str)) =
                config_node.print_string(yang2::data::DataFormat::XML, DataPrinterFlags::empty())
            {
                if let Ok(config) = decode_config_str_from_node(&config_str) {
                    return Ok(EditConfigData {
                        target,
                        default_operation,
                        test_option,
                        error_option,
                        config,
                    });
                }
            }
        }

        Err(Error::msg("invalid edit-config request"))
    }
}

#[derive(Debug, Clone)]
pub struct CopyConfigData {
    pub source: SourceType,
    pub target: SourceType,
}

impl TreeDecoder for CopyConfigData {
    fn decode_from(tree: &DataTree) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let mut target = SourceType::Startup;
        let mut source = SourceType::Running;

        // decode target
        if let Ok(target_node) = tree.find_path("/ietf-netconf:copy-config/target") {
            for ele in target_node.children() {
                match ele.schema().name() {
                    "startup" => target = SourceType::Startup,
                    "running" => target = SourceType::Running,
                    "candidate" => target = SourceType::Candidate,
                    _ => (),
                }
            }
        }

        // decode source
        if let Ok(source_node) = tree.find_path("/ietf-netconf:copy-config/source") {
            for ele in source_node.children() {
                match ele.schema().name() {
                    "startup" => source = SourceType::Startup,
                    "running" => source = SourceType::Running,
                    "candidate" => source = SourceType::Candidate,
                    _ => (),
                }
            }
        }

        Ok(CopyConfigData { source, target })
    }
}

#[derive(Debug, Clone)]
pub struct DeleteConfigData {
    pub target: SourceType,
}

impl TreeDecoder for DeleteConfigData {
    fn decode_from(tree: &DataTree) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let mut target = SourceType::Startup;

        // decode target
        if let Ok(target_node) = tree.find_path("/ietf-netconf:delete-config/target") {
            for ele in target_node.children() {
                match ele.schema().name() {
                    "startup" => target = SourceType::Startup,
                    "running" => target = SourceType::Running,
                    "candidate" => target = SourceType::Candidate,
                    _ => (),
                }
            }
        }

        Ok(DeleteConfigData { target })
    }
}

pub fn decode_filter_nodes_to_paths(filter_node: DataNodeRef, paths: &mut Vec<(String, String)>) {
    match filter_node.meta().find(|n| n.name().eq("type")) {
        Some(type_meta) => match type_meta.value() {
            "xpath" => {
                if let Some(select_meta) = filter_node.meta().find(|m| m.name().eq("select")) {
                    paths.push((select_meta.value().to_string(), "".to_string()))
                }
            }
            "subtree" => {
                if let Ok(Some(xml)) = filter_node.print_string(
                    yang2::data::DataFormat::XML,
                    DataPrinterFlags::KEEP_EMPTY_CONT,
                ) {
                    info!("filter: {}", xml);
                    decode_subtree_filter_paths(&xml, paths);
                }
            }
            _ => return,
        },
        _ => return,
    }
}

pub fn decode_subtree_filter_paths(xml_str: &str, paths: &mut Vec<(String, String)>) {
    let mut reader = NsReader::from_str(xml_str);
    if let Ok((ResolveResult::Bound(Namespace(NETCONF_BASE)), Event::Start(_bs))) =
        reader.read_resolved_event()
    {
        // let mut path_ns = p_ns.clone();
        loop {
            // 解析filter内容
            let filter_evt = reader.read_resolved_event();
            match filter_evt {
                Ok((ResolveResult::Bound(ns), Event::Start(febs))) => {
                    let qname = febs.to_end();
                    let prefix =
                        format!("/{}", String::from_utf8(qname.name().0.to_vec()).unwrap());
                    let ns = String::from_utf8(ns.into_inner().to_vec()).unwrap();
                    let content = String::from_utf8(
                        reader.read_text(qname.name()).unwrap().as_bytes().to_vec(),
                    )
                    .unwrap();
                    for ele in decode_subtree_filter_path(content, &prefix) {
                        paths.push((ele, ns.clone()));
                    }
                }
                Ok((ResolveResult::Bound(ns), Event::Empty(febs))) => {
                    let ns = String::from_utf8(ns.0.to_vec()).unwrap();
                    paths.push((
                        format!("/{}", String::from_utf8(febs.name().0.to_vec()).unwrap()),
                        ns,
                    ));
                }
                Ok((_, Event::Eof)) => break,
                _ => (),
            }
        }
    }
}

pub fn decode_config_str_from_node(config_node: &str) -> Result<String, Error> {
    let mut reader = NsReader::from_str(config_node);
    if let Ok(Event::Start(bs)) = reader.read_event() {
        if let Ok(data) = reader.read_text(bs.to_end().name()) {
            let config_str = String::from_utf8(data.into_owned().as_bytes().to_vec())?;
            return Ok(config_str);
        }
    }
    Err(Error::msg("read config str from config node xml failed"))
}
